\chapter{Ownership}
所有权（系统）是 Rust 最为与众不同的特性。它让 Rust 无需垃圾回收（garbage collector）即可保障内存安全，因此理解 Rust 中所有权如何工作是十分重要的。本章，我们将讲到所有权以及相关功能：借用（borrowing）、slice 以及 Rust 如何在内存中布局数据。
\section{什么是所有权?}
Rust 的核心功能（之一）是 所有权（ownership）。虽然该功能很容易解释，但它对语言的其他部分有着深刻的影响。所有程序都必须管理其运行时如何使用计算机内存。一些语言中具有垃圾回收机制，在程序运行时有规律地寻找不再使用的内存；在另一些语言中，程序员必须亲自分配和释放内存。Rust 则选择了第三种方式：通过所有权系统管理内存，编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则，程序都不能编译。在运行时，所有权系统的任何功能都不会让你的程序运行变慢。因为所有权对很多程序员来说都是一个新概念，需要一些时间来适应。好消息是随着你对 Rust 和所有权系统的规则越来越有经验，你就越能自然地编写出安全和高效的代码。持之以恒！

当你理解了所有权，你将有一个坚实的基础来理解那些使 Rust 独特的功能。在本章中，你将通过完成一些示例来学习所有权，这些示例基于一个常用的数据结构：字符串。

\section{栈（Stack）与堆（Heap）}\label{sec:stack_and_heap}
在很多语言中，你并不需要经常考虑到栈与堆。不过在像 Rust 这样的系统编程语言中，值是位于栈上还是堆上在更大程度上影响了语言的行为以及为何必须做出这样的抉择。我们会在本章的稍后部分描述所有权与栈和堆相关的内容，所以这里只是一个用来预热的简要解释。

栈和堆都是代码在运行时可供使用的内存，但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 \textbf{后进先出}（last in, first out）。

想象一下一叠盘子：当增加更多盘子时，把它们放在盘子堆的顶部，当需要盘子时，也从顶部拿走。不能从中间也不能从底部增加或拿走盘子！增加数据叫做 \textbf{进栈}（pushing onto the stack），而移出数据叫做 \textbf{出栈}（popping off the stack）。栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据，要改为存储在堆上。 堆是缺乏组织的：当向堆放入数据时，你要请求一定大小的空间。内存分配器（memory allocator）在堆的某处找到一块足够大的空位，把它标记为已使用，并返回一个表示该位置地址的 \textbf{指针}（pointer）。这个过程称作\textbf{在堆上分配内存}（allocating on the heap），有时简称为 “\textbf{分配}”（allocating）。（将数据推入栈中并不被认为是分配）。因为指向放入堆中数据的指针是已知的并且大小是固定的，你可以将该指针存储在栈上，不过当需要实际数据时，必须访问指针。想象一下去餐馆就座吃饭。当进入时，你说明有几个人，餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了，他们也可以通过询问来找到你们坐在哪。入栈比在堆上分配内存要快，因为（入栈时）分配器无需为存储新数据去搜索内存空间；其位置总是在栈顶。相比之下，在堆上分配内存则需要更多的工作，这是因为分配器必须首先找到一块足够存放数据的内存空间，并接着做一些记录为下一次分配做准备。访问堆上的数据比访问栈上的数据慢，因为必须通过指针来访问。现代处理器在内存中跳转越少就越快（缓存）。继续类比，假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜，接着桌子 B 听一个菜，然后再桌子 A，然后再桌子 B 这样的流程会更加缓慢。出于同样原因，处理器在处理的数据彼此较近的时候（比如在栈上）比较远的时候（比如可能在堆上）能更好的工作。当你的代码调用一个函数时，传递给函数的值（包括可能指向堆上数据的指针）和函数的局部变量被压入栈中。当函数结束时，这些值被移出栈。

跟踪哪部分代码正在使用堆上的哪些数据，最大限度的减少堆上的重复数据的数量，以及清理堆上不再使用的数据确保不会耗尽空间，这些问题正是所有权系统要处理的。一旦理解了所有权，你就不需要经常考虑栈和堆了，不过明白了所有权的主要目的就是管理堆数据，能够帮助解释为什么所有权要以这种方式工作。
\section{所有权规则}
首先，让我们看一下所有权的规则。当我们通过举例说明时，请谨记这些规则：
\begin{itemize}
    \item Rust 中的每一个值都有一个 所有者（owner）。
    \item 值在任一时刻有且只有一个所有者。
    \item 当所有者（变量）离开作用域，这个值将被丢弃。
\end{itemize}
\section{变量作用域}
既然我们已经掌握了基本语法，将不会在之后的例子中包含 main 函数 代码，所以如果你是从开始学习到这里的，必须手动将之后例子的代码放入一个 main 函数中才能运行。不写main函数，例子将显得更加简明，这样我们可以关注实际细节而不是样例代码。

在所有权的第一个例子中，我们看看一些变量的 \textbf{作用域}（scope）。作用域是一个项（item）在程序中有效的范围。假设有这样一个变量：
\begin{rust}
let s = "hello";
\end{rust}
变量 s 绑定到了一个字符串字面值，这个字符串值是硬编码进程序代码中的。这个变量从声明的点开始直到当前作用域结束时都是有效的。示例 \ref{code:variable_and_scope} 中的注释标明了变量 s 在何处是有效的。
\begin{listing}[!htbp]
\begin{rust}
    {                      // s 在这里无效，它尚未声明
        let s = "hello";   // 从此处起，s 是有效的
        // 使用 s
    }                      // 此作用域已结束，s 不再有效
\end{rust}
    \caption{一个变量和其有效的作用域}
    \label{code:variable_and_scope}
\end{listing}
换句话说，这里有两个重要的时间点：
\begin{itemize}
    \item 当 s 进入作用域时，它就是有效的。
    \item 这一直持续到它离开作用域为止。
\end{itemize}
目前为止，变量是否有效与作用域的关系跟其他编程语言是类似的。现在我们在此基础上介绍 String 类型。
\subsection{String 类型}
为了演示所有权的规则，我们需要一个比第三章 “\nameref{sec:data_type}” 中讲到的都要复杂的数据类型。前面介绍的类型都是已知大小的，可以存储在栈中，并且当离开作用域时被移出栈，如果代码的另一部分需要在不同的作用域中使用相同的值，可以快速简单地复制它们来创建一个新的独立实例。不过我们需要寻找一个存储在堆上的数据来探索 Rust 是如何知道该在何时清理数据的。

我们会专注于 String 与所有权相关的部分。这些方面也同样适用于标准库提供的或你自己创建的其他复杂数据类型。在第八章会更深入地讲解 String。

我们已经见过字符串字面值，即被硬编码进程序里的字符串值。字符串字面值是很方便的，不过它们并不适合使用文本的每一种场景。原因之一就是它们是不可变的。另一个原因是并非所有字符串的值都能在编写代码时就知道：例如，要是想获取用户输入并存储该怎么办呢？为此，Rust 有另一种字符串类型，String。这个类型管理被分配到堆上的数据，所以能够存储在编译时未知大小的文本。可以使用 from 函数基于字符串字面值来创建 String，如下：
\begin{rust}
let s = String::from("hello");
\end{rust}
这两个冒号 \textbf{::} 是运算符，允许将特定的 from 函数置于 String 类型的命名空间（namespace）下，而不需要使用类似 string\_from 这样的名字。在“\nameref{sec:method_syntex}”（“Method Syntax”） 部分会着重讲解这个语法，而且在第七章的 “路径用于引用模块树中的项” 中会讲到模块的命名空间。可以修改此类字符串：
\begin{rust}
    let mut s = String::from("hello");

    s.push_str(", world!"); // push_str() 在字符串后追加字面值

    println!("{}", s); // 将打印 `hello, world!`
\end{rust}
那么这里有什么区别呢？为什么 String 可变而字面值却不行呢？区别在于两个类型对内存的处理上。
\section{内存与分配}
就字符串字面值来说，我们在编译时就知道其内容，所以文本被直接硬编码进最终的可执行文件中。这使得字符串字面值快速且高效。不过这些特性都只得益于字符串字面值的不可变性。不幸的是，我们不能为了每一个在编译时大小未知的文本而将一块内存放入二进制文件中，并且它的大小还可能随着程序运行而改变。对于 String 类型，为了支持一个可变，可增长的文本片段，需要在堆上分配一块在编译时未知大小的内存来存放内容。这意味着：
\begin{itemize}
    \item 必须在运行时向\textbf{内存分配器}（memory allocator）请求内存。
    \item 需要一个当我们处理完 String 时将内存返回给分配器的方法。
\end{itemize}
第一部分由我们完成：当调用 String::from 时，它的\textbf{实现} (implementation) 请求其所需的内存。这在编程语言中是非常通用的。然而，第二部分实现起来就各有区别了。在有 \textbf{垃圾回收}（garbage collector，GC）的语言中，GC 记录并清除不再使用的内存，而我们并不需要关心它。在大部分没有 GC 的语言中，识别出不再使用的内存并调用代码显式释放就是我们的责任了，跟请求内存的时候一样。从历史的角度上说正确处理内存回收曾经是一个困难的编程问题。如果忘记回收了会浪费内存。如果过早回收了，将会出现无效变量。如果重复回收，这也是个 bug。我们需要精确的为一个 allocate 配对一个 free。Rust 采取了一个不同的策略：内存在拥有它的变量离开作用域后就被自动释放。下面是示例 \coderef{code:scope_with_var} 中作用域例子的一个使用 String 而不是字符串字面值的版本：
\begin{listing}[H]
\begin{rust}
    {
        let s = String::from("hello"); // 从此处起，s 是有效的

        // 使用 s
    }                                  // 此作用域已结束，
                                       // s 不再有效
\end{rust}
    \caption{字符串s的生命周期}
    \label{code:scope_with_var}
\end{listing}
这是一个将 String 需要的内存返回给分配器的很自然的位置：当 s 离开作用域的时候。当变量离开作用域，Rust 为我们调用一个特殊的函数。这个函数叫做 drop，在这里 String 的作者可以放置释放内存的代码。Rust 在结尾的 \} 处自动调用 drop。
\begin{note}
    在 C++ 中，这种 item 在生命周期结束时释放资源的模式有时被称作 资源获取即初始化（Resource Acquisition Is Initialization (RAII)）。如果你使用过 RAII 模式的话应该对 Rust 的 drop 函数并不陌生。
\end{note}
这个模式对编写 Rust 代码的方式有着深远的影响。现在它看起来很简单，不过在更复杂的场景下代码的行为可能是不可预测的，比如当有多个变量使用在堆上分配的内存时。现在让我们探索一些这样的场景。变量与数据交互的方式（一）：移动
在 Rust 中，多个变量可以采取不同的方式与同一数据进行交互。让我们看看示例 \ref{code:swap_data_of_variable} 中一个使用整型的例子。
\begin{listing}[H]
\begin{rust}
    let x = 5;
    let y = x;
\end{rust}
    \caption{将变量x的值赋值给y}
    \label{code:swap_data_of_variable}
\end{listing}
我们大致可以猜到这在干什么：“将 5 绑定到 x；接着生成一个值 x 的拷贝并绑定到 y”。现在有了两个变量，x 和 y，都等于 5。这也正是事实上发生了的，因为整数是有已知固定大小的简单值，所以这两个 5 被放入了栈中。现在看看这个 String 版本：
\begin{listing}[!]
    \label{code:swap_string}
\begin{rust}
    let s1 = String::from("hello");
    let s2 = s1;
\end{rust}
    \caption{赋值变量s1给s2}
\end{listing}
这看起来与上面的代码非常类似，所以我们可能会假设它们的运行方式也是类似的：也就是说，第二行可能会生成一个 s1 的拷贝并绑定到 s2 上。不过，事实上并不完全是这样。
\begin{figure}[!htbp]
    \centering
    \includegraphics[width=0.5\textwidth]{string_mem_layout}
    \caption{hello 字符串绑定的内存示意图}
    \label{fig:string_mem_layout}
\end{figure}
看看 \figref{fig:string_mem_layout} 以了解 String 的底层会发生什么。String 由三部分组成，左侧所示：一个指向存放字符串内容内存的指针、一个字符串长度、一个字符串容量。这一组数据存储在栈上。右侧则是堆上存放内容的内存部分。

长度表示 String 的内容当前使用了多少字节的内存。容量是 String 从分配器总共获取了多少字节的内存。长度与容量的区别是很重要的，不过在当前上下文中并不重要，所以现在可以忽略容量。当我们将 s1 赋值给 s2，String 的数据被复制了，这意味着我们从栈上拷贝了它的指针、长度和容量。我们并没有复制指针指向的堆上数据。换句话说，内存中数据的表现如\figref{fig:assign_string_to_another}所示。
\begin{figure}[!htbp]
    \centering
    \includegraphics[width=0.5\textwidth]{assign_string_to_another}
    \caption{字符串赋值的内存示意图}
    \label{fig:assign_string_to_another}
\end{figure}

\begin{figure}[H]
    \centering
    \includegraphics[width=0.5\textwidth]{string_deep_copy}
    \caption{字符串深拷贝}
    \label{fig:string_deep_copy}
\end{figure}
这个表现形式看起来 并不像 \figref{fig:string_deep_copy} 中的那样，如果 Rust 也拷贝了堆上的数据，那么内存看起来就是这样的。如果 Rust 这么做了，那么操作 s2 = s1 在堆上数据比较大的时候会对运行时性能造成非常大的影响。之前我们提到过当变量离开作用域后，Rust 自动调用 drop 函数并清理变量的堆内存。不过\figref{fig:assign_string_to_another} 展示了两个数据指针指向了同一位置。这就有了一个问题：当 s2 和 s1 离开作用域，它们都会尝试释放相同的内存。这是一个叫做 \textbf{二次释放（double free）}的错误，也是之前提到过的内存安全性 bug 之一。两次释放（相同）内存会导致内存污染，它可能会导致潜在的安全漏洞。为了确保内存安全，在 let s2 = s1; 之后，Rust 认为 s1 不再有效，因此 Rust 不需要在 s1 离开作用域后清理任何东西。看看在 s2 被创建之后尝试使用 s1 会发生什么；这段代码无法通过编译！
\begin{rust}
    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}, world!", s1);
\end{rust}
你会得到一个类似如下的错误，因为 Rust 禁止你使用无效的引用。
\begin{bash}
$ cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:5:28
  |
2 |     let s1 = String::from("hello");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |
5 |     println!("{}, world!", s1);
  |                            ^^ value borrowed here after move
  |
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` due to previous error
\end{bash}
如果你在其他语言中听说过术语 \textbf{浅拷贝}（shallow copy）和 \textbf{深拷贝}（deep copy），那么拷贝指针、长度和容量而不拷贝数据可能听起来像浅拷贝。不过因为 Rust 同时使第一个变量无效了，这个操作被称为 \textbf{移动}（move），而不是叫做浅拷贝。上面的例子可以解读为 s1 被 移动 到了 s2 中。那么具体发生了什么，如\figref{fig:string_deep_copy} 所示。

\begin{figure}[H]
    \centering
    \includegraphics[width=0.5\textwidth]{string_ownership_shift}
    \caption{所有权转移}
    \label{fig:string_ownership_shift}
\end{figure}
这样就解决了我们的问题！因为只有 s2 是有效的，当其离开作用域，它就释放自己的内存，完毕。另外，这里还隐含了一个设计选择：Rust 永远也不会自动创建数据的 “深拷贝”。因此，任何 \textbf{自动} 的复制都可以被认为是对运行时性能影响较小的。


\section{变量与数据交互的方式（二）：克隆}
如果我们 确实 需要深度复制 String 中堆上的数据，而不仅仅是栈上的数据，可以使用一个叫做 clone 的通用函数。第五章会讨论方法语法，不过因为方法在很多语言中是一个常见功能，所以之前你可能已经见过了。这是一个实际使用 clone 方法的例子：
\begin{rust}
    let s1 = String::from("hello");
    let s2 = s1.clone();

    println!("s1 = {}, s2 = {}", s1, s2);
\end{rust}
这段代码能正常运行，并且明确产生\figref{fig:string_deep_copy} 中行为，这里堆上的数据 \textbf{确实} 被复制了。当出现 clone 调用时，你应该知道一些消耗资源的的代码在被执行。

只在栈上的数据：拷贝
这里还有一个没有提到的小窍门。这些代码使用了整型并且是有效的，它们是 \coderef{code:stack_value_copy} 中的一部分：
\begin{listing}[!htbp]
\begin{rust}
    let x = 5;
    let y = x;

    println!("x = {}, y = {}", x, y);
\end{rust}
\caption{拷贝栈数据}
\label{code:stack_value_copy}
\end{listing}
但这段代码似乎与我们刚刚学到的内容相矛盾：没有调用 clone，不过 x 依然有效且没有被移动到 y 中。
原因是像整型这样的在编译时已知大小的类型被整个存储在栈上，所以拷贝其实际的值速度更快。这意味着没有理由在创建变量 y 后使 x 无效。换句话说，这里没有深浅拷贝的区别，所以这里调用 clone 并不会与通常的浅拷贝有什么不同，我们可以不用管它。

Rust 有一个叫做 Copy trait 的特殊注解，可以用在类似整型这样的存储在栈上的类型上（第十章将会详细讲解 trait）。如果一个类型实现了 Copy trait，那么一个旧的变量在将其赋值给其他变量后仍然可用。

Rust 不允许自身或其任何部分实现了 Drop trait 的类型使用 Copy trait。如果我们对其值离开作用域时需要特殊处理的类型使用 Copy 注解，将会出现一个编译时错误。要学习如何为你的类型添加 Copy 注解以实现该 trait，请阅读附录 C 中的 “可派生的 trait”。

那么哪些类型实现了 Copy trait 呢？你可以查看给定类型的文档来确认，不过作为一个通用的规则，任何一组简单标量值的组合都可以实现 Copy，任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型：
\begin{itemize}
    \item 所有整数类型，比如 u32。
    \item 布尔类型，bool，它的值是 true 和 false。
    \item 所有浮点数类型，比如 f64。
    \item 字符类型，char。
    \item 元组，当且仅当其包含的类型也都实现 Copy 的时候。比如，(i32, i32) 实现了 Copy，但 (i32, String) 就没有。
\end{itemize}
\section{所有权与函数}
将值传递给函数与给变量赋值的原理相似。向函数传递值可能会移动或者复制，就像赋值语句一样。\ref{code:ownership_of_function} 使用注释展示变量何时进入和离开作用域：
\begin{listing}[H]
\caption{带有所有权和作用域注释的函数}
\begin{rust}
fn main() {
    let s = String::from("hello");  // s 进入作用域
    takes_ownership(s);             // s 的值移动到函数里 ...
                                    // ... 所以到这里不再有效
    let x = 5;                      // x 进入作用域
    makes_copy(x);                  // x 应该移动函数里，
                                    // 但 i32 是 Copy 的，
                                    // 所以在后面可继续使用 x
} // 这里，x 先移出了作用域，然后是 s。但因为 s 的值已被移走，
  // 没有特殊之处

fn takes_ownership(some_string: String) { // some_string 进入作用域
    println!("{}", some_string);
} // 这里，some_string 移出作用域并调用 `drop` 方法。
  // 占用的内存被释放

fn makes_copy(some_integer: i32) { // some_integer 进入作用域
    println!("{}", some_integer);
} // 这里，some_integer 移出作用域。没有特殊之处
\end{rust}
    \label{code:ownership_of_function}
\end{listing}
当尝试在调用 takes\_ownership 后使用 s 时，Rust 会抛出一个编译时错误。这些静态检查使我们免于犯错。试试在 main 函数中添加使用 s 和 x 的代码来看看哪里能使用它们，以及所有权规则会在哪里阻止我们这么做。
\section{返回值与作用域}
返回值也可以转移所有权。\ref{code:ownership_of_return_value} 展示了一个返回了某些值的示例，与\ref{code:ownership_of_function} 一样带有类似的注释。
\begin{listing}[H]
    \caption{返回值的所有权}
\begin{rust}
fn main() {
    let s1 = gives_ownership();         // gives_ownership 将返回值
                                        // 转移给 s1
    let s2 = String::from("hello");     // s2 进入作用域
    let s3 = takes_and_gives_back(s2);  // s2 被移动到
                                        // takes_and_gives_back 中，
                                        // 它也将返回值移给 s3
} // 这里，s3 移出作用域并被丢弃。s2 也移出作用域，但已被移走，
  // 所以什么也不会发生。s1 离开作用域并被丢弃

fn gives_ownership() -> String {             // gives_ownership 会将
                                             // 返回值移动给
                                             // 调用它的函数
    let some_string = String::from("yours"); // some_string 进入作用域。
    some_string                              // 返回 some_string 
                                             // 并移出给调用的函数
                                             // 
}

// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
                                                      // 

    a_string  // 返回 a_string 并移出给调用的函数
}
\end{rust}
    \label{code:ownership_of_return_value}
\end{listing}
变量的所有权总是遵循相同的模式：将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时，其值将通过 drop 被清理掉，除非数据被移动为另一个变量所有。虽然这样是可以的，但是在每一个函数中都获取所有权并接着返回所有权有些啰嗦。如果我们想要函数使用一个值但不获取所有权该怎么办呢？如果我们还要接着使用它的话，每次都传进去再返回来就有点烦人了，除此之外，我们也可能想返回函数体中产生的一些数据。我们可以使用元组来返回多个值，如\ref{code:swap_data_of_variable} 所示。
\begin{listing}[H]
 \begin{rust}
fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() 返回字符串的长度

    (s, length)
}

\end{rust}   
    \caption{返回值延长所有权}
    \label{code:return_value_extent_ownership}
\end{listing}
但是这种实现不够优雅，Rust 对此提供了一个不用获取所有权就可以使用值的功能，叫做 \nameref{ch:ref_and_bor}。
\section{引用(references)与借用（borrow）}\label{ch:ref_and_bor}
\ref{code:swap_data_of_variable} 中的元组代码有这样一个问题：我们必须将 String 返回给调用函数，以便在调用 calculate\_length 后仍能使用 String，因为 String 被移动到了 calculate\_length 内。相反我们可以提供一个 String 值的引用（reference）。引用（reference）像一个指针，因为它是一个地址，我们可以由此访问储存于该地址的属于其他变量的数据。 与指针不同，引用确保指向某个特定类型的有效值。下面是如何定义并使用一个（新的）calculate\_length 函数，它以一个对象的引用作为参数而不是获取值的所有权：
\begin{listing}[H]
    \caption{传入引用}
    \begin{rust}
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}    
\end{rust}
\label{code:reference_with_string}
\end{listing}
首先，注意变量声明和函数返回值中的所有元组代码都消失了。其次，注意我们传递 \&s1 给 calculate\_length，同时在函数定义中，我们获取 \&String 而不是 String。这些 \& 符号就是 引用，它们允许你使用值但不获取其所有权。\figref{fig:reference_of_string} 展示了一张示意图。
\begin{figure}
    \begin{center}
        \includegraphics[width=0.5\textwidth]{reference_of_string}
    \end{center}
    \caption{引用字符串示意图}
    \label{fig:reference_of_string}
\end{figure}

\begin{note}
    与使用 \& 引用相反的操作是 \textbf{解引用}（dereferencing），它使用解引用运算符\*。我们将会在第八章遇到一些解引用运算符，并在第十五章详细讨论解引用。
\end{note}
仔细看看这个函数调用：
\begin{rust}
let s1 = String::from("hello");
let len = calculate_length(&s1);
\end{rust}
\&s1 语法让我们创建一个 指向 值 s1 的引用，但是并不拥有它。因为并不拥有这个值，所以当引用停止使用时，它所指向的值也不会被丢弃。同理，函数签名使用 \& 来表明参数 s 的类型是一个引用。让我们增加一些解释性的注释：
\begin{listing}[H]
    \begin{rust}
fn calculate_length(s: &String) -> usize { // s 是 String 的引用
    s.len()
} // 这里，s 离开了作用域。但因为它并不拥有引用值的所有权，
  // 所以什么也不会发生
    \end{rust}
\end{listing}
变量 s 有效的作用域与函数参数的作用域一样，不过当 s 停止使用时并不丢弃引用指向的数据，\textcolor{red}{因为 s 并没有所有权}。当函数使用引用而不是实际值作为参数，无需返回值来交还所有权，因为就不曾拥有所有权。我们将创建一个引用的行为称为 \textbf{借用}（borrowing）。正如现实生活中，如果一个人拥有某样东西，你可以从他那里借来。当你使用完毕，必须还回去。我们并不拥有它。如果我们尝试修改借用的变量呢？尝试\ref{code:change_value_of_borrow} 中的代码。这段代码无法通过编译！
\begin{listing}[H]
\begin{rust}
fn main() {
    let s = String::from("hello");

    change(&s);
}

fn change(some_string: &String) {
    some_string.push_str(", world");
}
\end{rust}
    \caption{尝试修改借用的值}
    \label{code:change_value_of_borrow}
\end{listing}
这里是错误：
\begin{bash}
$ cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference
 --> src/main.rs:8:5
  |
7 | fn change(some_string: &String) {
  |                        ------- help: consider changing this to be a mutable reference: `&mut String`
8 |     some_string.push_str(", world");
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable

For more information about this error, try `rustc --explain E0596`.
error: could not compile `ownership` due to previous error
\end{bash}
正如变量默认是不可变的，引用也一样。（默认）不允许修改引用的值。
\section{可变引用}
我们通过一个小调整就能修复\ref{code:change_value_of_borrow} 代码中的错误，允许我们修改一个借用的值，这就是 \textbf{可变引用}（mutable reference）：
\begin{listing}[H]
    \caption{可变引用}
    \begin{rust}
fn main() {
    let mut s = String::from("hello");

    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

    \end{rust}
    \label{code:mutable_reference}
\end{listing}
首先，我们必须将 s 改为 mut。然后在调用 change 函数的地方创建一个可变引用 \&mut s，并更新函数签名以接受一个可变引用 some\_string: \&mut String。这就非常清楚地表明，change 函数将改变它所借用的值。可变引用有一个很大的限制：如果你有一个对该变量的可变引用，你就不能再创建对该变量的引用。\textcolor{red}{尝试创建两个 s 的可变引用的代码会失败}，这段代码无法通过编译！
\begin{rust}
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;

    println!("{}, {}", r1, r2);
\end{rust}
错误如下：
\begin{bash}
$ cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0499]: cannot borrow `s` as mutable more than once at a time
 --> src/main.rs:5:14
  |
4 |     let r1 = &mut s;
  |              ------ first mutable borrow occurs here
5 |     let r2 = &mut s;
  |              ^^^^^^ second mutable borrow occurs here
6 |
7 |     println!("{}, {}", r1, r2);
  |                        -- first borrow later used here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership` due to previous error
\end{bash}
这个报错说这段代码是无效的，因为我们不能在同一时间多次将 s 作为可变变量借用。第一个可变引用 r1 中，并且必须持续到在 println！中使用它，但是在那个可变引用的创建和它的使用之间，我们又尝试在 r2 中创建另一个可变引用，该引用借用与 r1 相同的数据。

这一限制以一种非常谨慎的方式允许可变性，防止同一时间对同一数据存在多个可变引用。新 Rustacean 们经常难以适应这一点，因为大部分语言中变量任何时候都是可变的。这个限制的好处是 Rust 可以在编译时就避免数据竞争。\textbf{数据竞争}（data race）类似于竞态条件，它可由这三个行为造成：
\begin{itemize}
    \item 两个或更多指针同时访问同一数据。
    \item 至少有一个指针被用来写入数据。
    \item 没有同步数据访问的机制。
\end{itemize}
数据竞争会导致未定义行为，难以在运行时追踪，并且难以诊断和修复；Rust 避免了这种情况的发生，因为它甚至不会编译存在数据竞争的代码！一如既往，可以使用大括号来创建一个新的作用域，以允许拥有多个可变引用，只是不能 \textbf{同时} 拥有：
\begin{rust}
    let mut s = String::from("hello");
    {
        let r1 = &mut s;
    } // r1 在这里离开了作用域，所以我们完全可以创建一个新的引用
    let r2 = &mut s;
\end{rust}
Rust 在同时使用可变与不可变引用时也采用的类似的规则。这些代码会导致一个错误，这段代码无法通过编译！
\begin{rust}
    let mut s = String::from("hello");
    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    let r3 = &mut s; // 有问题

    println!("{}, {}, and {}", r1, r2, r3);
\end{rust}
错误如下：
\begin{bash}
$ cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
 --> src/main.rs:6:14
  |
4 |     let r1 = &s; // no problem
  |              -- immutable borrow occurs here
5 |     let r2 = &s; // no problem
6 |     let r3 = &mut s; // BIG PROBLEM
  |              ^^^^^^ mutable borrow occurs here
7 |
8 |     println!("{}, {}, and {}", r1, r2, r3);
  |                                -- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` due to previous error
\end{bash}
我们 \textbf{也不能}在拥有不可变引用的同时拥有可变引用。不可变引用的用户不希望在他们的值被意外的改变！然而，多个不可变引用是可以的，因为没有哪个只读数据有能力影响到其他读取到的数据。注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。例如，因为最后一次使用不可变引用（println!)，发生在声明可变引用之前，所以如下代码是可以编译的：
\begin{rust}
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    println!("{} and {}", r1, r2);
    // 此位置之后 r1 和 r2 不再使用

    let r3 = &mut s; // 没问题
    println!("{}", r3);
\end{rust}
不可变引用 r1 和 r2 的作用域在 println! 最后一次使用之后结束，这也是创建可变引用 r3 的地方。它们的作用域没有重叠，所以代码是可以编译的。编译器可以在作用域结束之前判断不再使用的引用。尽管这些错误有时使人沮丧，但请牢记这是 Rust 编译器在提前指出一个潜在的 bug（在编译时而不是在运行时）并精准显示问题所在。这样你就不必去跟踪为何数据并不是你想象中的那样。

\section{悬垂引用（Dangling References）}
在具有指针的语言中，很容易通过释放内存时保留指向它的指针而错误地生成一个 \textbf{悬垂指针}（dangling pointer），所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下，在 Rust 中编译器确保引用永远也不会变成悬垂状态：当你拥有一些数据的引用，编译器确保数据不会在其引用之前离开作用域。让我们尝试创建一个悬垂引用，Rust 会通过一个编译时错误来避免：
\begin{listing}[H]
    \caption{Dangling References}
    \begin{rust}
fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");
    &s
        // 此时s的作用域结束，指向hello的内存被释放，但是&s的地址还是会被返回
}
    \end{rust}
    \label{code:dangling_references}
\end{listing}
\begin{bash}
$ cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0106]: missing lifetime specifier
 --> src/main.rs:5:16
  |
5 | fn dangle() -> &String {
  |                ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
  |
5 | fn dangle() -> &'static String {
  |                 +++++++

For more information about this error, try `rustc --explain E0106`.
error: could not compile `ownership` due to previous error
\end{bash}
错误信息引用了一个我们还未介绍的功能：\textbf{生命周期}（lifetimes）。第十章会详细介绍生命周期。不过，如果你不理会生命周期部分，错误信息中确实包含了为什么这段代码有问题的关键信息：
this function's return type contains a borrowed value, but there is no value for it to be borrowed from 让我们仔细看看我们的 dangle 代码的每一步到底发生了什么：
\begin{listing}[H]
    \caption{dangle引用}
    \begin{rust}
fn dangle() -> &String { // dangle 返回一个字符串的引用

    let s = String::from("hello"); // s 是一个新字符串

    &s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放。
  // 危险！
    \end{rust}
    \label{code:dangling_ref}
\end{listing}
因为 s 是在 dangle 函数内创建的，当 dangle 的代码执行完毕后，s 将被释放。不过我们尝试返回它的引用。这意味着这个引用会指向一个无效的 String，这可不对！Rust 不会允许我们这么做。这里的解决方法是直接返回 String：
\begin{listing}[H]
    \caption{返回字符串延长变量生命周期}
    \begin{rust}
fn no_dangle() -> String {
    let s = String::from("hello");
    s
}
    \end{rust}
    \label{code:lifetime_extent}
\end{listing}
这样就没有任何错误了。所有权被移动出去，所以没有值被释放。
\section{引用的规则}
让我们概括一下之前对引用的讨论：
\begin{itemize}
    \item 在任意给定时间，要么 \textbf{只能有一个可变引用}，要么 \textbf{只能有多个不可变引用}。
\item 引用必须总是有效的。
\end{itemize}
\section{slice}
slice 允许你引用集合中一段连续的元素序列，而不用引用整个集合。slice 是一种引用，所以它没有所有权。这里有一个编程小习题：编写一个函数，该函数接收一个用空格分隔单词的字符串，并返回在该字符串中找到的第一个单词。如果函数在该字符串中并未找到空格，则整个字符串就是一个单词，所以应该返回整个字符串。让我们推敲下如何不用 slice 编写这个函数的签名，来理解 slice 能解决的问题：
\begin{rust}
fn first_word(s: &String) -> ?
\end{rust}
first\_word 函数有一个参数 \&String。因为我们不需要所有权，所以这没有问题。不过应该返回什么呢？我们并没有一个真正获取 \textbf{部分} 字符串的办法。不过，我们可以返回单词结尾的索引，结尾由一个空格表示。试试如\ref{code:index_of_space} 中的代码。
\begin{listing}[H]
    \caption{函数返回 String 参数的一个字节索引值}
    \begin{rust}
fn first_word(s: &String) -> usize {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }
    s.len()
}
    \end{rust}
    \label{code:index_of_space}
\end{listing}
因为需要逐个元素的检查 String 中的值是否为空格，需要用 as\_bytes 方法将 String 转化为字节数组。
\begin{rust}
    let bytes = s.as_bytes();
\end{rust}
接下来，使用 iter 方法在字节数组上创建一个迭代器：
\begin{rust}
    for (i, &item) in bytes.iter().enumerate() {
\end{rust}
我们将在第十四章章详细讨论迭代器。现在，只需知道 iter 方法返回集合中的每一个元素，而 enumerate 包装了 iter 的结果，将这些元素作为元组的一部分来返回。enumerate 返回的元组中，第一个元素是索引，第二个元素是集合中元素的引用。这比我们自己计算索引要方便一些。因为 enumerate 方法返回一个元组，我们可以使用模式来解构，我们将在第六章中进一步讨论有关模式的问题。所以在 for 循环中，我们指定了一个模式，其中元组中的 i 是索引而元组中的 \&item 是单个字节。因为我们从 .iter().enumerate() 中获取了集合元素的引用，所以模式中使用了 \&。在 for 循环中，我们通过字节的字面值语法来寻找代表空格的字节。如果找到了一个空格，返回它的位置。否则，使用 s.len() 返回字符串的长度：
\begin{rust}
        if item == b' ' {
            return i;
        }
    }

    s.len()
\end{rust}
现在有了一个找到字符串中第一个单词结尾索引的方法，不过这有一个问题。我们返回了一个独立的 usize，不过它只在 \&String 的上下文中才是一个有意义的数字。换句话说，因为它是一个与 String 相分离的值，无法保证将来它仍然有效。考虑一下\ref{code:change_value_after_slice} 中使用了\ref{code:index_of_space} 中 first\_word 函数的程序。
\begin{listing}[H]
    \caption{存储 first\_word 函数调用的返回值并接着改变 String 的内容}
    \begin{rust}
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s); // word 的值为 5
    s.clear(); // 这清空了字符串，使其等于 ""
    // word 在此处的值仍然是 5，
    // 但是没有更多的字符串让我们可以有效地应用数值 5。word 的值现在完全无效！
}
    \end{rust}
    \label{code:change_value_after_slice}
\end{listing}
这个程序编译时没有任何错误，而且在调用 s.clear() 之后使用 word 也不会出错。因为 word 与 s 状态完全没有联系，所以 word 仍然包含值 5。可以尝试用值 5 来提取变量 s 的第一个单词，不过这是有 bug 的，因为在我们将 5 保存到 word 之后 s 的内容已经改变。我们不得不时刻担心 word 的索引与 s 中的数据不再同步，这很易出错！如果编写这么一个 second\_word 函数的话，管理索引这件事将更加容易出问题。它的签名看起来像这样：
\begin{rust}
fn second_word(s: &String) -> (usize, usize) {
\end{rust}
现在我们要跟踪\textbf{一个开始索引} 和 \textbf{一个结尾索引}，同时有了更多从数据的某个特定状态计算而来的值，但都完全没有与这个状态相关联。现在有三个飘忽不定的不相关变量需要保持同步。幸运的是，Rust 为这个问题提供了一个解决方法：字符串 slice。
\subsection{字符串 slice}
字符串 slice（string slice）是 String 中一部分值的引用，它看起来像这样：
\begin{rust}
    let s = String::from("hello world");

    let hello = &s[0..5];
    let world = &s[6..11];
\end{rust}
不同于整个 String 的引用，hello 是一个部分 String 的引用，由一个额外的 [0..5] 部分指定。可以使用一个由中括号中的 [starting\_index..ending\_index] 指定的 range 创建一个 slice，其中 starting\_index 是 slice 的第一个位置，ending\_index 则是 slice 最后一个位置的后一个值([start,end))。在其内部，slice 的数据结构存储了 slice 的开始位置和长度，长度对应于 ending\_index 减去 starting\_index 的值。所以对于 let world = \&s[6..11]; 的情况，world 将是一个包含指向 s 索引 6 的指针和长度值 5 的 slice。
\begin{figure}[H]
	\centering
	\includegraphics[width=0.6\textwidth]{slice_with_string}
	\caption{切片内存布局}
	\label{fig:slice_with_string}
\end{figure}
对于 Rust 的 .. range 语法，如果想要从索引 0 开始，可以不写两个点号之前的值。换句话说，如下两个语句是相同的：
\begin{rust}
let s = String::from("hello");

let slice = &s[0..2];
let slice = &s[..2];
\end{rust}
依此类推，如果 slice 包含 String 的最后一个字节，也可以舍弃尾部的数字。这意味着如下也是相同的：
\begin{rust}
let s = String::from("hello");

let len = s.len();

let slice = &s[3..len];
let slice = &s[3..];
\end{rust}
也可以同时舍弃这两个值来获取整个字符串的 slice。所以如下亦是相同的：
\begin{rust}
let s = String::from("hello");

let len = s.len();

let slice = &s[0..len];
let slice = &s[..];
\end{rust}
\begin{note}
    字符串 slice range 的索引必须位于有效的 UTF-8 字符边界内，如果尝试从一个多字节字符的中间位置创建字符串 slice，则程序将会因错误而退出。出于介绍字符串 slice 的目的，本部分假设只使用 ASCII 字符集；第八章的 “使用字符串储存 UTF-8 编码的文本” 部分会更加全面的讨论 UTF-8 处理问题。
\end{note}
在记住所有这些知识后，让我们重写 first\_word 来返回一个 slice。“字符串 slice” 的类型声明写作 \&str：
\begin{listing}[H]
    \caption{}
    \begin{rust}
fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}
    \end{rust}
    \label{}
\end{listing}
我们使用跟\ref{code:index_of_space} 相同的方式获取单词结尾的索引，通过寻找第一个出现的空格。当找到一个空格，我们返回一个字符串 slice，它使用字符串的开始和空格的索引作为开始和结束的索引。现在当调用 first\_word 时，会返回与底层数据关联的单个值。这个值由一个 slice 开始位置的引用和 slice 中元素的数量组成。second\_word 函数也可以改为返回一个 slice：
\begin{rust}
fn second_word(s: &String) -> &str {
\end{rust}
现在我们有了一个不易混淆且直观的 API 了，因为编译器会确保指向 String 的引用持续有效。还记得\ref{code:change_value_after_slice} 程序中，那个当我们获取第一个单词结尾的索引后，接着就清除了字符串导致索引就无效的 bug 吗？那些代码在逻辑上是不正确的，但却没有显示任何直接的错误。问题会在之后尝试对空字符串使用第一个单词的索引时出现。slice 就不可能出现这种 bug 并让我们更早的知道出问题了。使用 slice 版本的 first\_word 会抛出一个编译时错误：
\begin{listing}[H]
    \caption{不变引用之后清除数据}
    \begin{rust}
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    s.clear(); // 错误！
    println!("the first word is: {}", word);
}
    \end{rust}
    \label{code:ref_after_clean}
\end{listing}
这里是编译错误：
\begin{bash}
$ cargo run
   Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
  --> src/main.rs:18:5
   |
16 |     let word = first_word(&s);
   |                           -- immutable borrow occurs here
17 |
18 |     s.clear(); // error!
   |     ^^^^^^^^^ mutable borrow occurs here
19 |
20 |     println!("the first word is: {}", word);
   |                                       ---- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` due to previous error
\end{bash}
回忆一下借用规则，当拥有某值的不可变引用时，就不能再获取一个可变引用。因为 clear 需要清空 String，它尝试获取一个可变引用。在调用 clear 之后的 println! 使用了 word 中的引用，所以这个不可变的引用在此时必须仍然有效。Rust 不允许 clear 中的可变引用和 word 中的不可变引用同时存在，因此编译失败。Rust 不仅使得我们的 API 简单易用，也在编译时就消除了一整类的错误！
\subsection{字符串字面值就是 slice}
还记得我们讲到过字符串字面值被储存在二进制文件中吗？现在知道 slice 了，我们就可以正确地理解字符串字面值了：
\begin{rust}
let s = "Hello, world!";
\end{rust}
这里 s 的类型是 \&str：它是一个指向二进制程序特定位置的 slice。这也就是为什么字符串字面值是不可变的；\&str 是一个不可变引用。

\subsection{字符串 slice 作为参数}\label{sec:slice_as_param}
在知道了能够获取字面值和 String 的 slice 后，我们对 first\_word 做了改进，这是它的签名：
\begin{rust}
fn first_word(s: &String) -> &str {
\end{rust}
而更有经验的 Rustacean 会编写出\ref{code:ref_after_clean} 中的签名，因为它使得可以对 \&String 值和 \&str 值使用相同的函数：
\begin{rust}
fn first_word(s: &str) -> &str {
\end{rust}
如果有一个字符串 slice，可以直接传递它。如果有一个 String，则可以传递整个 String 的 slice 或对 String 的引用。这种灵活性利用了 deref coercions 的优势，这个特性我们将在“\nameref{sec:func_convert}”章节中介绍。定义一个获取字符串 slice 而不是 String 引用的函数使得我们的 API 更加通用并且不会丢失任何功能：
\begin{listing}[H]
    \caption{适用于String和str字面量的方法}
    \begin{rust}
fn main() {
    let my_string = String::from("hello world");

    // `first_word` 适用于 `String`（的 slice），部分或全部
    let word = first_word(&my_string[0..6]);
    let word = first_word(&my_string[..]);
    // `first_word` 也适用于 `String` 的引用，
    // 这等价于整个 `String` 的 slice
    let word = first_word(&my_string);

    let my_string_literal = "hello world";

    // `first_word` 适用于字符串字面值，部分或全部
    let word = first_word(&my_string_literal[0..6]);
    let word = first_word(&my_string_literal[..]);

    // 因为字符串字面值已经 **是** 字符串 slice 了，
    // 这也是适用的，无需 slice 语法！
    let word = first_word(my_string_literal);
}
    \end{rust}
    \label{}
\end{listing}
\subsection{其他类型的 slice}
字符串 slice，正如你想象的那样，是针对字符串的。不过也有更通用的 slice 类型。考虑一下这个数组：
\begin{rust}
let a = [1, 2, 3, 4, 5];
\end{rust}
就跟我们想要获取字符串的一部分那样，我们也会想要引用数组的一部分。我们可以这样做：
\begin{rust}
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
assert_eq!(slice, &[2, 3]);
\end{rust}
这个 slice 的类型是 \&[i32]。它跟字符串 slice 的工作方式一样，通过存储第一个集合元素的引用和一个集合总长度。你可以对其他所有集合使用这类 slice。第五章讲到 vector 时会详细讨论这些集合。所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存，但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。所有权系统影响了 Rust 中很多其他部分的工作方式，所以我们还会继续讲到这些概念，这将贯穿本书的余下内容。让我们开始第五章，来看看如何将多份数据组合进一个 struct 中。
% \begin{itemize}
% \item 指针安全原则：数据不应该 被同时alias和mutabe。这个原则的目标是确保数据alias的时候不能mutable。引用数据将导致数据临时只读直到引用不再使用。
% \item 只读引用：只读引用允许alias但是禁止mutation。
% \item  可写引用
% \end{itemize}
% 可写引用通过\&mut创建，如果有类型，可以写为\&mut i32。相比只读引用你可以看到两个重要的不同：
% \begin{enumerate}
%     \item  当变量num是只读引用的时候，vec仍然拥有读权限。现在num是可写引用后在num使用的时候vec将失去所有的的权限。
%     \item 当num是只读引用的时候，*num仅仅有读权限。现在num是一个可写引用,`*num`获得写权限。
% \end{enumerate}
% 对于可写引用的观察，mutable引用允许对数据修改但是阻止了alias。borrow path中vec临时不可用，因此没有alias。
% 第二个观察是什么是的可写引用有用。vec[2]可以通过\*num修改。例如\*num+=1。
% \begin{note}
%   注意\*num有写权限，但是num没有。可写引用可以临时降级为只读引用。
% \end{note}

% 上图：
% \begin{itemize}
%     \item  代码第一行vec拥有向量中的数据，并且可对数据读写。
% \item 代码第二行num为可写引用，引用vec中第二个元素。可写引用导致了在引用时刻，vec本身丧失了对数据的读写能力。但是引用 的数据所有权临时到了num手中。对数据的写 权限通过`*num`获取，对`*num`的权限变成了读写，但是并不拥有数据。
% \item 代码第三行将对`*num`的数据进行只读引用。因此num2对数据拥有读权限，同时`*num`的写权限失去。
% \end{itemize}

% > 📌上面的代码中我们通过\&*num实现移除\*num的写权限而不是读权限。

% \section{在引用生命周期末尾的权限返回}
% 在上面的程序中档引用使用的时候将会改变数据的a权限。使用描述的是引用的生命周期。

% % ![](image/image_N_wgFcStMA.png)

% 上面的例子中第二行`let y = \&x`开始只读引用的生命周期开始，y对数据有读权限但是失去了写权限。在`let z = *y`;之后只读引用的生命周期结束。x恢复对数据的读写和所有。

% % ![](image/image_QBTCrBBbtV.png)

% 上面的代码中`c`对`v`中的第一个元素只读引用，此时`v`将失去对数据的写权限，同时`c`获取对数据的读权限。接下来我们读取`c`判断其是否为ascii码，如果是则将其数据转换为大写之后赋值给`up`，此时`up`对转码之后的数据拥有读写。赋值之后只读引用的生命周期结束，`v`对向量中的数据重新恢复写权限，于是将`up`的值赋值给第一个元素。如果分支走到了else，`c`的生命周期同样结束，`v`恢复数据的写权限。

% \section{数据必须比引用活的久}

% % ![](image/image_Jl0HW2aNhe.png)

% 上面的代码中s\_ref是对s的引用，如果我们删除了s，但是printf又操作了s\_ref则报错。原因在于引用的生命周期长于了数据。为了捕获这类错误，rust使用了权限解决这一问题。`\&s`从s身上移除的所有权。然而`drop`操作执行的时候需要`s`对数据拥有权限，这就导致了权限不匹配。下面有一段u安全的函数代码：

% % ![](image/image_HDYdmbcbUt.png)

% 上面的代码引用了一种新的flow权限`F`。`F`期待输入引用(`\&strings[0]`)的表达式返回输出引用的表达式(`s\_ref`)。F并不改变整个函数体，一个引用又F权限的时候，他被使用中也有F权限。例如：

% % ![](image/image_Yhiv3pt_Jo.png)

% 上面的函数将编译不过，因为表达式`\&strings[0]`和`default`缺乏需要的F权限。错误信息如下：
% \begin{bash}
% error[E0106]: missing lifetime specifier
%  --> test.rs:1:57
%   |
% 1 | fn first_or(strings: &Vec<String>, default: &String) -> &String {
%   |                      ------------           -------     ^ expected named lifetime parameter
%   |
%   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `strings` or `default`
% \end{bash}
% 上述例子中，如果rust仅仅查看函数前面，不知道是否输出\&string是strings还是default的引用。为了理解这点的重要性，让我们看看下面的代码：
% \begin{rust}
% fn main() {
%     let strings = vec![];
%     let default = String::from("default");
%     let s = first_or(&strings, &default);
%     drop(default);
%     println!("{}", s);
% }
% \end{rust}
% 上面的程序中`first\_or`函数允许default去flow进返回值。drop删除default之后打印s将会出错，rust只允许飞default flow导返回值。

% % ![](image/image_OAjODH4BZM.png)

% 上面的代码也是不安全的，因为引用\&s将在return 之后不可用。

% \section{切片规则}
% \begin{itemize}
%     \item [0..5]：表示索引从0到5，如果 [..5] 省略第一个元素则 表示起点为0，如果 [..] 省略起点和终点。
%     \item [..5]
% \end{itemize}
% 者规则：
% \begin{itemize}
% \item Rust中每个值有一个拥有者。
% \item 同一时间只能有一个拥有者。
% \item 当拥有者出了scope之后，值将被删掉。
% \end{itemize}
% \begin{rust}
% {// s当前不可用，还没有声明
%   let s = "hello";//s是一个字面量字符串的指针，可用
% }//s不可用，因为出了s的scope
% \end{rust}
% \section{字符串字面量和String字符串的区别}
% \begin{enumerate}
%     \item 字面量字符串`str`不可修改，`String`可以(需要声明为`mut `) 
%     \item `str`在编译期就知道大小和内容，而String在运行期才知道。
% \end{enumerate}
% \section{变量和数据的Move}
% \begin{rust}
% let x = 5;//x = 5
% let y = x;//y=5
% \end{rust}
% 对于基础数据类型因为他们是在编译期分配的，所以赋值的时候都是传值，但是对非基础类型则不同。在rust中allocator分配堆计算的操作赋值的时候经常涉及到`drop`操作。
% \begin{rust}
% fn main() {
%     let s1 = String::from("hello world");
%     let s2 = s1;// 此操作之后S1将不再拥有数据
%     println!("s1 = {s1}");//编译出错
% }
% \end{rust}
% % ![String结构说明](image/image_gO837wfR-B.png "String结构说明")

% 代码第三行的赋值操作：

% % ![String的赋值操作](image/image_ONnBa1TF_J.png "String的赋值操作")

% 上述操作我们赋值本质上只是拷贝了编译期决定的len,capacity等栈上变量，指向堆数据的指针ptr所指定的数据并没有被复制（主要原因在于复制的代价太大了）。如上图现在由两个指针指向了相同的堆数据，出了scope会导致内存被两次释放。为了确保内存安全在复制之后，Rust认为s1不再可用，这样Rust在出Scope之后不需要释放s1的内存。这个操作在Rust中被称为Move。

% 上面的代码第四行会编译报错，原因是：第二行生成的s1是在heap上分配的，s1是指向一堆字符串的首位。第三行赋值操作后s2指向了s1的这块内存，为了放置 s1和s2作用于结束的时候两次删除分配的空间，这里s1相当于将数据借给了s2，以至于自己没有了这块数据，所以第四行打印的时候就会出错。如果第2行换成`let s1="hello world"`则不会出错，原因是对字符串字面量是在编译期决定的，不用分配heap，而是栈空间。此错误产生类似于C++中的浅拷贝，如果需要拷贝则可以按照调用`s2 = s1.clone()`实现s1和s2拥有完全相同的内容。

% `rust对堆数据使用move操作对栈数据使用拷贝操作`

% > 📌常规数据类型报错u32,bool,f64,char，tuple（里面全是这么几种数据类型时）等数据通常使用拷贝操作

% rust 有一个Copy trait，实现此trait的对象在赋值的时候将使用拷贝而不是move。

% \section{Clone}
% 如果我们拷贝数据的时候想拷贝堆里面的数据，我们可以调用方法clone。
% \begin{rust}
% let s1 = String::from("hello");
% let s2 = s1.clome();
% \end{rust}
% \section{栈数据只有拷贝}
% 编译期能确定的数据会放在栈里，因此拷贝实际的值将很快，这意味着我们没有任何理由阻止复制后，赋值之前的值。Rust有一些特别的称为Copy的Trait截图放类型在栈上。如果我们实现了Copy Trait，变量将不会被move而是拷贝。实现为Copy的一些类型：
% \begin{itemize}
%     \item 所有的整数类型，如`u32`
% \item bool类型
% \item float数据类型,如f64
% \item 字符类型，如char。
% \item tuple，如果仅仅包含实现了Copy的类型，例如`(i32,i32)`实现了copy，但是`(i32,String)`没有。
% \end{itemize}
% \section{函数和ownership}
% \begin{rust}
% fn main() {
%     let s = String::from("hello world");
%     takes_ownership(s);
%     println!("s = {s}");//错误
%     let x = 5;
%     make_copy(5);

% }
% fn takes_ownership(some_str:String){//some_str进入scope
%     println!("{}",some_str);

% }// some_str 出scope，head被释放
% fn make_copy(v:i32){
%     println!("{v}");
% }

% \end{rust}
% 错误原因在于s在函数调用的过程中所有权被转移，println之后在函数作用域结束，变量s会被销毁。导致第四行打印此变量时出错。
% \section{返回值和scope}
% \begin{rust}
% fn main() {
%     let s1 = give_ownership();
%     let s2 = String::from("hello");
%     let s3 = take_and_gives_back(s2);
% }
% fn give_ownership() -> String {
%     let something_string = String::from("yours");
%     something_string
% }
% fn take_and_gives_back(a_string: String) -> String {
%     a_string
% }
% \end{rust}
% 函数第6行创建一个字符串后将字符串转移到函数外的变量s1.
% \begin{rust}
% fn main() {
%     let s1 = String::from("hello world");
%     let (s2, len) = calculate_length(s1);
%     println!("The length of '{}' is{}", s2, len);
% }
% fn calculate_length(s1: String) -> (String, usize) {
%     let len = s1.len();
%     (s1, len)
% }
% \end{rust}
% 上述多参数返回的时候使用tuple。

% \section{rust编译器不完美}
% rust编译器在判断分支的时候
% \begin{rust}
% let x = if true {1} else {"hello world"};
% \end{rust}
% 在上面的代码中条件`true`总是满足，那么x的值为1。但是编译器会假设其他分支也可能被执行，这样编译器就认为x既可能是i32也可能是string，从而报编译错误。
% \begin{rust}
% fn move_to(s1: String, s2: String) -> String {
%     s1
% }

% fn main(){
%     let (s1,s2) = (String::from("hello"),String::from("world"));
%     let s3 = move_to(s1,s2);
%     println!("{} {}",s2,s3);
% }
% \end{rust}
% 第8行报编译错误，原因在于在move\_two 函数中s2没有没使用，s2 的值被转移后消失，第8行依然尝试访问这个消失的值。
% \section{rust中的引用(borrow)}
% \begin{rust}
% fn string_length(s1: &String) -> usize {
%     s1.len()
% }
% fn main() {
%     let s: String = String::from("hello world");
%     let len = string_length(&s);
%     println!("{s} = {len}");
% }
% \end{rust}
% \&s创建了一个s的引用，其并不拥有s，只是s的一个别名，因此在调用len后并不会被删除。
% \begin{rust}
% fn add_str(s: &String) {
%     s.push_str("world");
% }
% fn main() {
%     let a = String::from("hello");
%     add_str(&a);
% }
% \end{rust}
% 上述代码中rust中应用的值不能被修改（引用的值类型为const）。如果想要修改引用的值可以这样：
% \begin{rust}
% fn add_str(s: &mut String) {
%     s.push_str("world");
% }
% fn main() {
%     let mut a = String::from("hello");
%     add_str(&mut a);
% }
% \end{rust}
% \begin{itemize}
%     \item 引用差异：和C++不同在于rust中表达式\&s表示引用常量s的值，\&mut s表示引用变量的值。
% \item 变量引用的限制:如果你有一个变量引用值，你就不能再次引用它。
% \end{itemize}
% \begin{rust}
% let mut s = String::from("hello");

% let r1 = &mut s;
% let r2 = &mut s;

% println!("{}, {}", r1, r2);
% \end{rust}
% 重复引用变量会导致，第一个引用如果修改了值那么第二个引用的值应该是啥？rust直接拒绝此做法，但是你可以这样：
% \begin{rust}
% let mut s = String::from("hello");

%   {
%       let r1 = &mut s;
%   } // r1 goes out of scope here, so we can make a new reference with no problems.

%   let r2 = &mut s;
% \end{rust}
% 上述操作成立，原因是r1的scope中它borrow了s，在scope结束后s1不再存在，r2可以继续borrow s。rust拒绝对一个变量的常量和变量引用：
% \begin{rust}
% fn main() {
%     let mut a = String::from("hello");
%     let b = &a;
%     let c = &a;
%     let d = &mut a;
%     println!("b = {b} c = {c}");//报错
% }
% \end{rust}
% 注意上述代码只有加上打印那一行才会报错，原因在于打印的时候由两个变量均从a brrow了数据，而且他们一个对数据能修改，一个不能。如果brrow数据的时候只有一种情况：能修改或者不能修改是可以的：
% \begin{rust}
% fn main() {
%     let mut a = String::from("hello");
%     let b = &a;
%     let c = &a;
%     println!("b = {b} c = {c}");
%     let d = &mut a;
%     println!("d = {d}");
% }
% \end{rust}
% 注意基础数据类型的引用在使用的时候需要使用`*`解引用。在包含指针的语言中，悬挂指针都是一个非常危险的操作。在rust中不会出现悬挂引用，原因是：`对数据的引用将被保证在数据的scope中被使用`。下面这段代码会编译报错：
% \begin{rust}
% fn dangling() -> &String {
%     let a = String::from("hello world");
%     &a
% }
% fn main() {
%     let r = dangling();
% }
% \end{rust}
% 原因是在dangling的scope中创建了一个String，然后\&a 借用了他。在出了scope之后原始值被销毁导致引用悬挂。

% immutable reference(也称为shared references)
% mutable reference 也称为(unique references)引用

